//Copyright 2012 Google Inc. All Rights Reserved.

/**
 * GA VIZ JAVASCRIPT LIBRARY v4.4
 *
 * @author
 * Shan Aminzadeh, shan.aminzadeh@gmail.com
 * Arya Bondarian, aryabond@gmail.com
 * Albert Gau, dirtbagzfan@gmail.com
 * Travis Lai, travisrlai@gmail.com
 * Daniel Nguyen, danielnuwin@gmail.com
 *
 * @fileoverview
 * This library is designed to help translate Google Analytics API queries
 * into Google Visualizations seamlessly.
 * For help using this document, visit:
 * http://qrgen.site90.com/google/user-doc.html.
 *
 * @license
 * This work is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation.
 */

/**
 * Namespace of the library.
 */
var gaViz = {};

/**
 * Refers to the API scope that the user will need authentication for.
 * @const {String}
 */
gaViz.SCOPES = 'https://www.googleapis.com/auth/analytics.readonly';

/**
 * List of Chart Objects that will hold all of the visualizations if they can't
 * be rendered yet.
 * @type {array}
 */
gaViz.listOfCharts = [];


/**
 * Starting function that should check authentication.
 * Does not use the namespace gaViz
 */
handleClientLoad = function() {
    // Load the Visualization API and the piechart package.
    gapi.client.setApiKey(gaViz.apiKey);
    window.setTimeout(gaViz.checkAuth, 1);
};


/**
 * A function that sets the API key and Client ID passed by the user.
 * @param {object} settings - Contains the API Key and Client ID variables.
 */
gaViz.configAccount = function(settings) {
    /**
     * API Key from Google Console
     * @type {String}
     */
    gaViz.apiKey = settings.apiKey;

    /**
     * Client Key from Google Console
     * @type {String}
     */
    gaViz.clientId = settings.clientId;
};

// Loads the core chart and table from the Google Visualization libraries
google.load('visualization', '1', {'packages': ['corechart', 'table']});

/**
 * Does an automated check to see if the user is authenticated.
 */
gaViz.checkAuth = function() {
    gapi.auth.authorize(
        {client_id: gaViz.clientId, scope: gaViz.SCOPES, immediate: true},
        gaViz.handleAuthResult);
};

/**
 *
 * @param {object} authResult - The result of the authentication attempt. If the
 * auth passes, then the library loads the charts. If it the authResult doesn't
 * pass, then a button is enabled that allows the user to authenticate.
 */
gaViz.handleAuthResult = function(authResult) {
    var authorizeButton = document.getElementById('authorize-button');
    if (authResult) {
        authorizeButton.style.visibility = 'hidden';

        // Load Google Analytics
        gapi.client.load('analytics', 'v3', function() {

            gaViz.clientLoaded = true;
            // Render each chart in the array list
            for (var i = 0; i < gaViz.listOfCharts.length; i++) {
                var chart = gaViz.listOfCharts[i];
                chart.render();
            }
        });

    } else {
        authorizeButton.style.visibility = '';
        authorizeButton.onclick = gaViz.handleAuthClick;
    }
};

/**
 *
 * Checks to see if user is authenticated, calls handleAuthResult
 * @return {boolean} false.
 * @param {object} event - event when button is clicked.
 */
gaViz.handleAuthClick = function(event) {
    gapi.auth.authorize({client_id: gaViz.clientId, scope: gaViz.SCOPES,
        immediate: false}, gaViz.handleAuthResult);
    return false;
};

/**
 * A Chart object is the primary object in the library.
 * @param {object} config - Config is the object that defines.
 * @constructor
 */
gaViz.Chart = function(config) {
    this.config = config; // Object that contains all parameters

    // if client is loaded, render the chart
    // else, add it to a queue of charts to render
    if (gaViz.clientLoaded)
        this.render();
    else
        gaViz.listOfCharts.push(this);
};

/**
 * Puts the response into dataTable object.
 * @param {object} resp - A Google Analytics response.
 * @return {object} data - A Google Data Table filled with the response.
 */
gaViz.Chart.prototype.getDataTable = function(resp) {
    var data = new google.visualization.DataTable();
    var numOfColumns = resp.columnHeaders.length;
    var numResults = resp.totalResults;

    for (var i = 0; i < numOfColumns; i++) {
        var dataType = resp.columnHeaders[i].dataType;
        var name = resp.columnHeaders[i].name;

        if (name == 'ga:date' &&
            !(this.config.type == 'column' || this.config.type == 'bar')) {
            dataType = 'date';
        } else if (dataType == 'STRING') {
            dataType = 'string';
        } else if (dataType == 'INTEGER') {
            dataType = 'number';
        }
        data.addColumn(dataType, this.formatMetricDim(name));
    }

    for (var i = 0; i < numResults; i++) {
        var arrayMetrics = [];

        for (var j = 0; j < numOfColumns; j++) {
            var name = resp.columnHeaders[j].name;
            var dataType = resp.columnHeaders[j].dataType;

            if (name == 'ga:date' &&
                !(this.config.type == 'column' || this.config.type == 'bar')) {
                arrayMetrics.push(this.stringToDate(resp.rows[i][j]));
            } else if (dataType == 'INTEGER') {
                arrayMetrics.push(parseInt(resp.rows[i][j]));
            } else {
                arrayMetrics.push(resp.rows[i][j]);
            }
        }
        data.addRow(arrayMetrics);
    }
    return data;
};

/**
 * Function that checks the library defined type and retrieves the corresponding
 * Google Visualization. If no type is defined, then the visualization defaults
 * to table.
 * @return {object} new google.visualization -
 * returns the corresponding Google Visualization.
 */
gaViz.Chart.prototype.getChart = function() {

    this.validateParam();
    var div = document.getElementById(this.config.divContainer);

    if (this.config.type == 'line')
        return new google.visualization.LineChart(div);
    else if (this.config.type == 'bar')
        return new google.visualization.BarChart(div);
    else if (this.config.type == 'pie')
        return new google.visualization.PieChart(div);
    else if (this.config.type == 'column')
        return new google.visualization.ColumnChart(div);
    else
        return new google.visualization.Table(div);
};

/**
 * Populates the chart with the data from the data table.
 * @param {object} chart - The chart object you wish to draw the datat into.
 * @param {object} dataTable - The Google data table holding the response.
 */
gaViz.Chart.prototype.draw = function(chart, dataTable) {
    chart.draw(dataTable, this.config.chartOptions);
};

/**
 * Default callback for creating visualizations with a response.
 * First the response is put into a data table, and the corresponding
 * chart is retrieved. The two are then combined into a chart populated
 * with the data.
 * @param {object} resp - A Google Analytics API response.
 */
gaViz.Chart.prototype.defaultCallback = function(resp) {

    if (resp.error) {
        this.divError('RENDER ERROR: ' + resp.error.code +
            ' ' + resp.error.message);
    }

    // converts response to data table
    var dataTable = this.getDataTable(resp);
    // returns a chart object based on this.config.chartType
    var chart = this.getChart();
    // simple draw method. Developers could write their own here.
    this.draw(chart, dataTable);
};

/**
 * Binds a method to it's object.
 * @param {Object} object The main object to bind to.
 * @param {Object} method The method to bind to the object.
 * @return {Function} the function passed in boound to the object parameter.
 */
gaViz.Chart.prototype.bindMethod = function(object, method) {
    return function() {
        return method.apply(object, arguments);
    };
};

/**
 * Executes the Google Analytics request and then executes the callback with
 * the request.
 */
gaViz.Chart.prototype.render = function() {

    if (this.config.lastNtime) {
        this.config.query['end-date'] = this.dateToString(new Date());
        this.config.query['start-date'] = this.setStartDate();
    }

    var request = gapi.client.analytics.data.ga.get(this.config.query);

    if (this.config.callback)
        request.execute(this.bindMethod(this, this.config.callback));
    else
        request.execute(this.bindMethod(this, this.defaultCallback));
};

/**
 * Checks for config options specified by this lib. If there are errors
 * then a error message is displayed.
 */
gaViz.Chart.prototype.validateParam = function() {

    if (!this.config.divContainer ||
        document.getElementById(this.config.divContainer) == null) {
        var errMsg = 'MISSING CHART PARAMETER: "divContainer" is missing';
        if (this.config.chartOptions.title)
            errMsg += ' in ' + this.config.chartOptions.title;
        alert(errMsg);
    }
};

/**
 * Print an error message in the specified div. If the div does not exist, the
 * message is printed in an alert.
 * @param {String} message - The message you wish to display.
 */
gaViz.Chart.prototype.divError = function(message) {
    if (this.config.divContainer) {
        var div = document.getElementById(this.config.divContainer);
        var errorText = document.createTextNode(message + '\r\n');
        var font = document.createElement('font');
        font.style.color = 'red';
        font.appendChild(errorText);
        div.appendChild(font);
    }
    else
        alert(message);
};

/**
 * Formats the Google Metrics and Dimensions into readable strings
 * that capitalizes the first letter and puts a space between
 * any capital letters. ie: ga:newVisits = New Visits
 * @param {String} metric - returns a metric.
 * @return {String} finalString - Metric/Dimension formatted nicely.
 */
gaViz.Chart.prototype.formatMetricDim = function(metric) {
    var strLen = metric.length;
    var index;
    var strings = new Array();
    var upper = false;
    var finalString;

    // Checks for upper case letters, character by character
    for (var i = 0; i < strLen; i++) {
        var character = metric.charAt(i);
        if (character == character.toUpperCase() && character != ':') {
            index = i;
            upper = true;
        }
    }

    // If there is a upper case letter found, then split it into separate words
    // Else, trim the first three characters ("ga:")
    if (!upper) {
        finalString = this.trim(metric);
    }
    else {
        //var tmp = index;
        strings[0] = metric.substring(3, index);
        strings[1] = metric.substring(index);

        finalString = strings[0].charAt(0).toUpperCase() +
            strings[0].slice(1) + ' ' + strings[1];
    }

    return finalString;
};

/**
 * Removes the "ga:" from the legend label and capitalizes the first letter
 * @param {String} string = The string that needs trimming.
 * @return {String} capital - the formatted string.
 */
gaViz.Chart.prototype.trim = function(string) {
    var name = string.substring(3);
    var capital = name.charAt(0).toUpperCase() + name.slice(1);

    return capital;
};

/**
 * Sets the dynamic start date when the metric will be displaying
 * a nTime segment
 * @return {String} dateToString(date) - The new start date that was set.
 */
gaViz.Chart.prototype.setStartDate = function() {

    var date = new Date();
    nTime = this.config.lastNtime;

    if (nTime[0] == 'ga:date' || nTime[0] == 'ga:day')
        date.setDate(date.getDate() - nTime[1]);
    else if (nTime[0] == 'ga:hour')
        date.setHours(date.getHours() - nTime[1]);
    else if (nTime[0] == 'ga:month')
        date.setMonth(date.getMonth() - nTime[1]);
    else if (nTime[0] == 'ga:year')
        date.setYear(date.getFullYear() - nTime[1]);
    else if (nTime[0] == 'ga:week')
        date.setDate(date.getDate() - (nTime[1] * 7));
    else {
        this.divError('Invalid lastNtime parameter. ' +
            'Should follow format [string:"ga:metric",int:"value"]');
    }
    return this.dateToString(date);
};


/**
 * Converts a String "YYYY-MM-DD" to a Date Object
 * @param {String} date - The string representation of the date.
 * @return {Date} date - A JS Date object.
 */
gaViz.Chart.prototype.stringToDate = function(date) {
    var year = date.substring(0, 4);
    var month = date.substring(4, 6);
    var day = date.substring(6, 8);

    if (month < 10) {
        month = month.substring(1, 2);
    }

    month = month - 1;

    if (day < 10) {
        day = day.substring(1, 2);
    }

    var dateObj = new Date(year, month, day);
    return dateObj;
};

/**
 * Converts a Date Object to a String "YYYY-MM-DD"
 * @param {Date} date - A JS Date object.
 * @return {String} - The string representation of the date.
 */
gaViz.Chart.prototype.dateToString = function(date) {
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();

    if (month < 10)
        month = '0' + month;
    if (day < 10)
        day = '0' + day;

    return (year + '-' + month + '-' + day);
};

// end of file



























